doto
巨集將第一個參數傳遞給其後運算式當作第一個參數。它將以下層疊的運算式:
(let [array (java.util.ArrayList.)]
(.add array 11)
(.add array 3)
(.add array 7)
array)
;; => [11 3 7]
轉換成:
(doto (java.util.ArrayList.)
(.add 11)
(.add 3)
(.add 7))
;; => [11 3 7]
使用 doto
簡化了許多繁瑣的步驟。
因爲 Java 類型的方法無法在 Clojure 當作一級函式,所以與 map
等函式配合時,需要多一層匿名函式包裝:
(map #(.length %) ["Happy" "New" "Year"])
;; => (5 3 4)
使用 memfn
可以將執行個體的方法轉換成 Clojure 中的函式,與高階函式搭配使用:
(map (memfn length) ["Happy" "New" "Year"])
;; => (5 3 4)
例外是程式運行中發生非預期的問題時,產生出來的類型。根據產生的例外,做適當的處理或善後,是健壯穩定的程式必要的條件。Clojure 使用在 Java 中處理例外的關鍵字 try
、catch
、finally
,作爲例外處理的建構單元:
(defn divide-by [denom]
(try
(/ 1 denom)
(catch ArithmeticException e
(.printStackTrace e))
(finally
(println "Divided by" denom))))
以上範例在 try
運算式內計算以 1 除以參數,如果發生 ArithmeticException
例外(除以 0),則會印出該例外的除錯堆疊資訊,finally
則是不論 try
運算式是否產生例外,都會進入 finally
運算式內運行。
Clojure 程式發生不可預期的錯誤時,可以使用 throw
來拋出例外:
(throw (IllegalStateException. "Illegal state exeption"))
;; => IllegalStateException Illegal state exeption
值得注意的是,使用 throw
丟出的例外類型,必須是 java.lang.Throwable
或是它的子類別。
大多數時間都是從 Clojure 端呼叫 Java,也會有要從 Java 端呼叫 Clojure 的時候。使用時首先引入 clojure.java.api.Clojure
類別,它提供了找尋 Var 物件、解析程式與存取命名空間的方法。
找到欲存取的 Var 物件後,以其實作的 IFn 介面進行調用,即可呼叫該 Var 物件代表的函式:
import clojure.java.api.Clojure;
import clojure.lang.IFn;
IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);
或是引入 Clojure 的命名空間
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));
或者是引入 Clojure 的高階函式來使用:
IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
map.invoke(inc, Clojure.read("[1 2 3]"));
從本篇文章中你了解到如何載入 Java 套件與類別,知道了讀取類別成員的方法,還有可以用來建立匿名與具名型別的巨集。還了解到如何將 Clojure 序列轉換成 Java 陣列的方式,以及例外處理的各個建構子。除此之外,還學會了如何由 Java 調用 Clojure 函式與資料的方式。
還不賴吧?今天就先到這裡,下一篇文章再見囉!
(本篇文章同步刊登於 GitHub,歡迎在文章下方留言或發送 PR 給予建議與指教)